home *** CD-ROM | disk | FTP | other *** search
- /*
- * CLIENT routines for Simple Mail Transfer Protocol ala RFC821
- * A.D. Barksdale Garbee II, aka Bdale, N3EUA
- * Copyright 1986 Bdale Garbee, All Rights Reserved.
- * Permission granted for non-commercial copying and use, provided
- * this notice is retained.
- * Modified 14 June 1987 by P. Karn for symbolic target addresses,
- * also rebuilt locking mechanism
- * Copyright 1987 1988 David Trulli, All Rights Reserved.
- * Permission granted for non-commercial copying and use, provided
- * this notice is retained.
- */
- /* Mods by G1EMM and PA0GRI */
- #include "global.h"
- #include "commands.h"
- #ifndef MSDOS
- #include <time.h>
- #include <setjmp.h>
- #endif
- #ifdef UNIX
- #ifdef BSD
- #undef BSD
- #endif
- #endif
- #include <sys/stat.h>
- #include <stdarg.h>
- #include "mbuf.h"
- #include "proc.h"
- #include "socket.h"
- #ifdef LZW
- #include "lzw.h"
- #else
- #include "tcp.h"
- #endif
- #include "smtp.h"
- #include "dirutil.h"
- #include "session.h"
- #include "mailutil.h"
-
- #if !defined(_lint)
- static char rcsid[] OPTIONAL = "$Id: smtpcli.c,v 1.33 1997/09/07 21:18:28 root Exp root $";
- #endif
-
- #ifdef HOPPER
- static int UseHopper = 0; /* G8FSL SMTP hopper default OFF */
- static int dohopper (int argc, char *argv[], void *p); /* g8fsl */
- #endif
-
- #ifdef UNIX
- extern void sm_status (int pos, char *str);
- #endif
-
- struct timer Smtpcli_t;
- static uint32 Gateway;
-
- int Rewritetrace = 0; /* used for rewrite trace mode */
-
- #ifdef SMTPTRACE
- static unsigned short Smtptrace = 0; /* used for trace level */
- static int dosmtptrace (int argc, char *argv[], void *p);
- static char smtp_recv[] = "smtpcli recv: %s\n";
- #endif
-
- static unsigned short Smtpmaxcli = MAXSESSIONS; /* the max client connections allowed */
- static int Smtpsessions = 0; /* number of client connections
- * currently open */
-
- #ifdef LZW
- int Smtpslzw = 1;
- static int Smtpclzw = 1;
- #endif
-
- static int Smtpbatch = 0;
-
- #ifdef MBFWD
- int Smtpbidcheck = 1;
- int Smtpheaders = 1;
- #endif
-
- int Smtpmode = 0;
- int Smtpquiet = 0;
- static int UseMX = 0; /* use MX records in domain lookup */
- int SMTPnotify = 1;
-
- static struct smtpcli *cli_session[MAXSESSIONS]; /* queue of client sessions */
-
- static int dorewritechk (int argc, char *argv[], void *p);
- static int dorewritetr (int argc, char *argv[], void *p);
- static void dosmtptick (int i, void *p1, void *p2);
- static void dosmtpkick (int i, void *p1, void *p2);
- static void del_job (struct smtp_job * jp);
- static void del_session (struct smtpcli * cb);
- static int dogateway (int argc, char *argv[], void *p);
- static int dosmtpmaxcli (int argc, char *argv[], void *p);
- static int dotimer (int argc, char *argv[], void *p);
- static int doquiet (int argc, char *argv[], void *p);
-
- #ifdef LZW
- static int doclzw (int argc, char *argv[], void *p);
- static int doslzw (int argc, char *argv[], void *p);
- #endif
-
- #ifdef HOLDMODS
- static int doholdscan (int argc, char *argv[], void *p);
- #endif
-
- static int donotify (int argc, char *argv[], void *p);
- static int dousemx (int argc, char *argv[], void *p);
- static int dosmtpkill (int argc, char *argv[], void *p);
- static int dosmtplist (int argc, char *argv[], void *p);
- static int dobatch (int argc, char *argv[], void *p);
- static void execjobs (void);
- static int getresp (struct smtpcli * ftp, char *usebuf, int mincode);
- static void logerr (struct smtpcli * cb, char *line);
- static struct smtpcli *lookup (uint32 destaddr);
- static struct smtpcli *newcb (void);
- static int next_job (struct smtpcli * cb);
- static void retmail (struct smtpcli * cb);
- static void sendcmd (struct smtpcli * cb, const char *fmt,...);
- static int smtpsendfile (struct smtpcli * cb);
- static int setsmtpmode (int argc, char *argv[], void *p);
- static void check_qtime (register struct smtpcli * cb);
- static struct smtp_job *setupjob (struct smtpcli * cb, char *id, char *from);
- static void smtp_send (int unused, void *cb1, void *p);
- static int smtpkick (int argc, char *argv[], void *p);
- static int dosmtpt4 (int argc, char *argv[], void *p);
-
- #ifdef MBFWD
- static int dobidcheck (int argc, char *argv[], void *p);
- static int doheaders (int argc, char *argv[], void *p);
- #endif
-
- static int dodtimeout (int argc, char *argv[], void *p);
-
- static struct cmds Smtpcmds[] =
- {
- { "batch", dobatch, 0, 0, NULLCHAR },
- #ifdef MBFWD
- { "bidcheck", dobidcheck, 0, 0, NULLCHAR },
- #endif
- { "dtimeout", dodtimeout, 0, 0, NULLCHAR },
- { "gateway", dogateway, 0, 0, NULLCHAR },
- #ifdef MBFWD
- { "headers", doheaders, 0, 0, NULLCHAR },
- #endif
- #ifdef HOLDMODS
- { "holdscanall", doholdscan, 0, 0, NULLCHAR },
- #endif
- #ifdef HOPPER
- { "hopper", dohopper, 0, 0, NULLCHAR },
- #endif
- { "kick", smtpkick, 0, 0, NULLCHAR },
- { "kill", dosmtpkill, 0, 2, "smtp kill <jobnumber>" },
- { "list", dosmtplist, 0, 0, NULLCHAR },
- { "maxclients", dosmtpmaxcli, 0, 0, NULLCHAR },
- { "mode", setsmtpmode, 0, 0, NULLCHAR },
- { "notify", donotify, 0, 0, NULLCHAR },
- { "quiet", doquiet, 0, 0, NULLCHAR },
- #ifdef LZW
- { "reclzw", doslzw, 0, 0, NULLCHAR },
- #endif
- { "rewritecheck", dorewritechk, 0, 2, "smtp rewritecheck address" },
- { "rewritetrace", dorewritetr, 0, 0, NULLCHAR },
- #ifdef LZW
- { "sendlzw", doclzw, 0, 0, NULLCHAR },
- #endif
- { "timer", dotimer, 0, 0, NULLCHAR },
- #ifdef SMTPTRACE
- { "trace", dosmtptrace, 0, 0, NULLCHAR },
- #endif
- { "t4", dosmtpt4, 0, 0, NULLCHAR },
- { "usemx", dousemx, 0, 0, NULLCHAR },
- { NULLCHAR, NULL, 0, 0, NULLCHAR }
- };
-
-
-
- int
- dosmtp (int argc, char *argv[], void *p)
- {
- return subcmd (Smtpcmds, argc, argv, p);
- }
-
-
-
- int Sholdscanall = 0;
-
- #ifdef HOLDMODS
- static int
- doholdscan (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Sholdscanall, "Scan all messages with wordhold/userhold files", argc, argv);
- }
- #endif
-
-
-
- static int Sdtimer = 0;
-
- static int
- dodtimeout (int argc, char *argv[], void *p OPTIONAL)
- {
- return setint (&Sdtimer, "Delivery timeout (hours)", argc, argv);
- }
-
-
-
- static int Smtpt4;
-
- static int
- dosmtpt4 (int argc, char *argv[], void *p OPTIONAL)
- {
- return setint (&Smtpt4, "SMTP T4", argc, argv);
- }
-
-
-
- static int
- dobatch (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Smtpbatch, "SMTP batching", argc, argv);
- }
-
-
-
- #ifdef MBFWD
- static int
- doheaders (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Smtpheaders, "SMTP RFC-822 headers in data for local BBS messages", argc, argv);
- }
-
-
-
- static int
- dobidcheck (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Smtpbidcheck, "SMTP bid checking", argc, argv);
- }
- #endif
-
-
-
- static int
- donotify (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&SMTPnotify, "SMTP incoming mail notification", argc, argv);
- }
-
-
-
- #ifdef LZW
- static int
- doclzw (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Smtpclzw, "SMTP send lzw", argc, argv);
- }
-
-
-
- static int
- doslzw (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Smtpslzw, "SMTP recv lzw", argc, argv);
- }
- #endif
-
-
-
- static int
- doquiet (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Smtpquiet, "SMTP quiet", argc, argv);
- }
-
-
-
- static int
- dousemx (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&UseMX, "MX records used", argc, argv);
- }
-
-
-
- static int
- dosmtpmaxcli (int argc, char *argv[], void *p OPTIONAL)
- {
- return setshort (&Smtpmaxcli, "Max clients", argc, argv);
- }
-
-
-
- static int
- setsmtpmode (int argc, char *argv[], void *p OPTIONAL)
- {
- if (argc < 2)
- tprintf ("smtp mode: %s\n", (Smtpmode & QUEUE) ? "queue" : "route");
- else {
- switch (*argv[1]) {
- case 'q':
- Smtpmode |= QUEUE;
- break;
- case 'r':
- Smtpmode &= ~QUEUE;
- break;
- default:
- tputs ("Usage: smtp mode [queue | route]\n");
- break;
- }
- }
- return 0;
- }
-
-
-
- static int
- dogateway (int argc, char *argv[], void *p OPTIONAL)
- {
- uint32 n;
-
- if (argc < 2)
- tprintf ("%s\n", inet_ntoa (Gateway));
- else if (!stricmp (argv[1], "none"))
- Gateway = 0;
- else if ((n = resolve (argv[1])) == 0) {
- tprintf (Badhost, argv[1]);
- return 1;
- } else
- Gateway = n;
- return 0;
- }
-
-
-
- #ifdef HOPPER
- static int
- dohopper (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&UseHopper, "G8FSL mail hopper", argc, argv);
- }
- #endif
-
-
-
- #ifdef SMTPTRACE
- static int
- dosmtptrace (int argc, char *argv[], void *p OPTIONAL)
- {
- return setshort (&Smtptrace, "SMTP tracing", argc, argv);
- }
- #endif
-
-
-
- static int
- dorewritechk (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
- {
- int oldtrace;
- char *destination;
-
- oldtrace = Rewritetrace;
- Rewritetrace = 1;
- destination = rewrite_address (argv[1], 0);
- Rewritetrace = oldtrace;
- tprintf ("Address '%s' rewrites to '%s'\n", argv[1], destination);
- free (destination);
- return 0;
- }
-
-
-
- static int
- dorewritetr (int argc, char *argv[], void *p OPTIONAL)
- {
- return setbool (&Rewritetrace, "Rewrite tracing", argc, argv);
- }
-
-
-
- /* list jobs waiting to be sent in the mqueue */
- static int
- dosmtplist (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
- {
- char tstring[80];
- char line[20];
- char host[LINELEN];
- char to[LINELEN];
- char from[LINELEN];
- char *cp;
- char status;
- int flowsave;
- struct stat stbuf;
- struct tm *tminfo;
- FILE *fp;
-
- flowsave = Current->flowmode;
- Current->flowmode = 1; /* Enable the more mechanism */
- tputs ("S Job Size Date Time Host From\n");
- (void) filedir (Mailqueue, 0, line);
- while (line[0] != '\0') {
- sprintf (tstring, "%s/%s", Mailqdir, line);
- if ((fp = fopen (tstring, READ_TEXT)) == NULLFILE) {
- tprintf ("Can't open %s: %s\n", tstring, SYS_ERRLIST(errno));
- continue;
- }
- if ((cp = strrchr (line, '.')) != NULLCHAR)
- *cp = '\0';
- sprintf (tstring, "%s/%s.lck", Mailqdir, line);
- if (access (tstring, 0))
- status = ' ';
- else
- status = 'L';
- sprintf (tstring, "%s/%s.txt", Mailqdir, line);
- if (stat (tstring, &stbuf) == -1) {
- sprintf (tstring, "%s/%s.lck", Mailqdir, line);
- unlink (tstring);
- sprintf (tstring, "%s/%s.wrk", Mailqdir, line);
- unlink (tstring);
- goto skip;
- }
- tminfo = localtime (&stbuf.st_ctime);
- host[0] = from[0] = 0;
- (void) fgets (host, sizeof (host), fp);
- rip (host);
- (void) fgets (from, sizeof (from), fp);
- rip (from);
- tprintf ("%c %7s %7ld %02d/%02d %02d:%02d %-20s %s\n ",
- status, line, (long) stbuf.st_size,
- tminfo->tm_mon + 1,
- tminfo->tm_mday,
- tminfo->tm_hour,
- tminfo->tm_min,
- host, from);
- while (fgets (to, sizeof (to), fp) != NULLCHAR) {
- rip (to);
- tprintf ("%s ", to);
- }
- tputc ('\n');
- skip:
- (void) fclose (fp);
- kwait (NULL);
- (void) filedir (Mailqueue, 1, line);
- }
- Current->flowmode = flowsave;
- return 0;
- }
-
-
-
- /* kill a job in the mqueue */
- static int
- dosmtpkill (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
- {
- char s[SLINELEN];
- char *cp, c;
-
- sprintf (s, "%s/%s.lck", Mailqdir, argv[1]);
- cp = strrchr (s, '.');
- if (cp != NULLCHAR) { /* shouldn't occur */
- if (!access (s, 0)) {
- Current->ttystate.echo = Current->ttystate.edit = 0;
- c = (char) keywait ("Warning, job is locked by SMTP. Remove (y/n)? ", 0);
- Current->ttystate.echo = Current->ttystate.edit = 1;
- if (c != 'y')
- return 0;
- (void) unlink (s);
- }
- strcpy (cp, ".wrk");
- if (unlink (s))
- tprintf ("Job id %s not found\n", argv[1]);
- strcpy (cp, ".txt");
- (void) unlink (s);
- }
- return 0;
- }
-
-
-
- /* Set outbound spool scan interval */
- static int
- dotimer (int argc, char *argv[], void *p OPTIONAL)
- {
- if (argc < 2) {
- tprintf ("smtp timer = %lu/%lu\n", read_timer (&Smtpcli_t) / 1000L,
- dur_timer (&Smtpcli_t) / 1000L);
- return 0;
- }
- Smtpcli_t.func = (void (*)(void *)) smtptick; /* what to call on timeout */
- Smtpcli_t.arg = NULL; /* dummy value */
- set_timer (&Smtpcli_t, atol (argv[1]) * 1000L); /* set timer duration */
- start_detached_timer (&Smtpcli_t); /* and fire it up */
- return 0;
- }
-
-
-
- static void
- dosmtpkick (int i OPTIONAL, void *p1, void *p2)
- {
- uint32 addr = 0;
-
- if ((int) p2 > 1 && (addr = resolve ((char *) p1)) == 0)
- tprintf (Badhost, (char *) p1);
- else
- dosmtptick (0, (void *) addr, (void *) 0);
- if (p1)
- free (p1);
- }
-
-
-
- static int
- smtpkick (int argc, char *argv[], void *p OPTIONAL)
- {
- (void) newproc ("smtp client", 1024, dosmtpkick, 0, (argc > 1) ? (void *) strdup (argv[1]) : (void *) 0, (void *) argc, 0);
- return 0;
- }
-
-
-
- void
- smtptick (void *t)
- {
- (void) newproc ("smtp client", 1024, dosmtptick, 0, t, (void *) 0, 0);
- }
-
-
-
- /* This is the routine that gets called every so often to do outgoing
- * mail processing. When called with a null argument, it runs the entire
- * queue; if called with a specific non-zero IP address from the remote
- * kick server, it only starts up sessions to that address.
- */
- static void
- dosmtptick (int t OPTIONAL, void *p1, void *p2 OPTIONAL)
- {
- register struct smtpcli *cb;
- struct smtp_job *jp;
- struct list *ap;
- char tmpstring[LINELEN], wfilename[13], prefix[9];
- char from[LINELEN], to[LINELEN];
- char *cp, *cp1;
- uint32 destaddr, target;
- FILE *wfile;
- static int Ssmtpcli = 0;
-
- target = (uint32) p1;
- #ifdef SMTPTRACE
- if (Smtptrace > 5)
- tcmdprintf ("smtp daemon entered, target = %s\n", inet_ntoa (target));
- #endif
- /* permit only one general purpose smtp client at a time */
- if (!target) {
- if (Ssmtpcli) {
- start_detached_timer (&Smtpcli_t);
- return;
- } else
- Ssmtpcli = 1;
- }
- kwait (NULL);
- for ((void) filedir (Mailqueue, 0, wfilename); wfilename[0] != '\0'; (void) filedir (Mailqueue, 1, wfilename)) {
-
- /* save the prefix of the file name which it job id */
- cp = wfilename;
- cp1 = prefix;
- while (*cp && *cp != '.')
- *cp1++ = *cp++;
- *cp1 = '\0';
-
- kwait (NULL);
-
- /* first check to see if the *.txt file is missing */
- sprintf (tmpstring, "%s/%s", Mailqdir, wfilename);
- strcpy (&tmpstring[strlen (tmpstring) - 3], "txt");
- if (access (tmpstring, 0)) {
- /* it's missing, remove *.wrk and *.lck (if exists) */
- sprintf (tmpstring, "%s/%s", Mailqdir, wfilename);
- unlink (tmpstring);
- rmlock (Mailqdir, prefix);
- continue;
- }
- /* lock this file from the smtp daemon */
- if (mlock (Mailqdir, prefix))
- continue;
-
- kwait (NULL);
- sprintf (tmpstring, "%s/%s", Mailqdir, wfilename);
- if ((wfile = fopen (tmpstring, READ_TEXT)) == NULLFILE) {
- /* probably too many open files */
- rmlock (Mailqdir, prefix);
- /* continue to next message. The failure may be temporary */
- continue;
- }
- (void) fgets (tmpstring, LINELEN, wfile); /* read target host */
- rip (tmpstring);
-
- kwait (NULL);
- if ((destaddr = mailroute (tmpstring)) == 0) {
- (void) fclose (wfile);
- tcmdprintf ("** smtp: Unknown address %s\n", tmpstring);
- rmlock (Mailqdir, prefix);
- continue;
- }
- if (target != 0 && destaddr != target) {
- (void) fclose (wfile);
- rmlock (Mailqdir, prefix);
- continue; /* Not the proper target of a kick */
- }
- kwait (NULL);
- if ((cb = lookup (destaddr)) == NULLSMTPCLI) {
- /* there are enough processes running already */
- if (Smtpsessions >= Smtpmaxcli) {
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf ("smtp daemon: too many processes\n");
- #endif
- log (-1, "smtp daemon: too many processes");
- (void) fclose (wfile);
- rmlock (Mailqdir, prefix);
- break;
- }
- if ((cb = newcb ()) == NULLSMTPCLI) {
- (void) fclose (wfile);
- rmlock (Mailqdir, prefix);
- kwait (NULL);
- break;
- }
- cb->ipdest = destaddr;
- cb->destname = strdup (tmpstring);
- } else {
- if (cb->lock) {
- /* This system is already is sending mail lets not
- * interfere with its send queue.
- */
- (void) fclose (wfile);
- rmlock (Mailqdir, prefix);
- continue;
- }
- }
-
- kwait (NULL);
- (void) fgets (from, LINELEN, wfile); /* read from */
- rip (from);
- if ((jp = setupjob (cb, prefix, from)) == NULLJOB) {
- (void) fclose (wfile);
- rmlock (Mailqdir, prefix);
- del_session (cb);
- break;
- }
- kwait (NULL);
- while (wfile != NULLFILE && fgets (to, LINELEN, wfile) != NULLCHAR) {
- rip (to);
- if (addlist (&jp->to, to, DOMAIN, to) == NULLLIST) {
- (void) fclose (wfile);
- wfile = NULLFILE;
- del_session (cb);
- }
- kwait (NULL);
- }
- if (wfile != NULLFILE) {
- (void) fclose (wfile);
- wfile = NULLFILE;
- }
- #ifdef SMTPTRACE
- if (Smtptrace > 1) {
- tcmdprintf ("queue job %s From: %s To:", prefix, from);
- for (ap = jp->to; ap != NULLLIST; ap = ap->next)
- tcmdprintf (" %s", ap->val);
- tcmdprintf ("\n");
- }
- #endif
- }
-
- /* start sending that mail */
- execjobs ();
- kwait (NULL);
-
- /* Restart timer */
- start_detached_timer (&Smtpcli_t);
- Ssmtpcli = 0;
- return;
- }
-
-
-
- /* This is the master state machine that handles a single SMTP transaction.
- * It is called with a queue of jobs for a particular host.
- * The logic is complicated by the "Smtpbatch" variable, which controls
- * the batching of SMTP commands. If Smtpbatch is true, then many of the
- * SMTP commands are sent in one swell foop before waiting for any of
- * the responses. Unfortunately, this breaks many brain-damaged SMTP servers
- * out there, so provisions have to be made to operate SMTP in lock-step mode.
- */
- static void
- smtp_send (int unused OPTIONAL, void *cb1, void *p OPTIONAL)
- {
- register struct smtpcli *cb;
- register struct list *tp;
- struct sockaddr_in fsocket;
- char const *cp;
- uint32 Altmx[5];
- int rcode;
- int rcpts;
- int goodrcpt;
- int i;
- int smtpbatch;
- int init = 1;
- #ifdef LZW
- int lzwmode, lzwbits, ampr;
- #endif
-
- cb = (struct smtpcli *) cb1;
- cb->lock = 1;
- fsocket.sin_family = AF_INET;
- fsocket.sin_addr.s_addr = cb->ipdest;
- fsocket.sin_port = IPPORT_SMTP;
-
- cb->s = socket (AF_INET, SOCK_STREAM, 0);
- (void) sockmode (cb->s, SOCK_ASCII);
- (void) setflush (cb->s, -1); /* We'll explicitly flush before reading */
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf ("SMTP client Trying...\n");
- #endif
- /* Set a timeout for this connection */
- kalarm (Smtpt4 * 1000L);
- if (connect (cb->s, (char *) &fsocket, SOCKSIZE) != 0) {
- kalarm (0L);
- (void) shutdown (cb->s, 2); /* K2MF: To make sure it doesn't linger around */
- close_s (cb->s);/* to make sure it's closed */
-
- /* Selcuk: Let's try other MX's before Gateway */
- if (UseMX && fsocket.sin_addr.s_addr != Gateway
- && fsocket.sin_addr.s_addr != Ip_addr) {
- if (resolve_amx (cb->destname, fsocket.sin_addr.s_addr, Altmx)) {
- #ifdef SMTPTRACE
- if (Smtptrace > 1)
- tcmdprintf ("SMTP client trying MX...\n");
- #endif
- for (i = 0; Altmx[i]; i++) {
- fsocket.sin_addr.s_addr = Altmx[i];
- /* n5knx: don't deliver to self, that's a loop! Also, to avoid indirect
- loops we ignore sites with lower preference than ourselves */
- if (ismyaddr (fsocket.sin_addr.s_addr))
- break;
- cb->s = socket (AF_INET, SOCK_STREAM, 0);
- (void) sockmode (cb->s, SOCK_ASCII);
- (void) setflush (cb->s, -1);
- kalarm (Smtpt4 * 1000L);
- if (connect (cb->s, (char *) &fsocket, SOCKSIZE) == 0)
- goto connected;
- else {
- kalarm (0L);
- (void) shutdown (cb->s, 2); /* K2MF: To make sure it doesn't linger around */
- close_s (cb->s);
- }
- }
- }
- }
- if (Smtpt4 && Gateway && (fsocket.sin_addr.s_addr != Gateway)
- #ifdef HOPPER
- && (Gateway != Ip_addr) /* g8fsl via g0mhd */
- # endif
- ) {
- /* Try it via the gateway */
- fsocket.sin_addr.s_addr = Gateway;
- cb->s = socket (AF_INET, SOCK_STREAM, 0);
- (void) sockmode (cb->s, SOCK_ASCII);
- (void) setflush (cb->s, -1); /* We'll explicitly flush before reading */
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf ("SMTP client Trying gateway...\n");
- #endif
- /* Set a timeout for this connection */
- kalarm (Smtpt4 * 1000L);
- if (connect (cb->s, (char *) &fsocket, SOCKSIZE) != 0) {
- kalarm (0L);
- cp = sockerr (cb->s);
- (void) shutdown (cb->s, 2); /* K2MF: To make sure it doesn't linger around */
- close_s (cb->s); /* to make sure it's closed */
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf ("Connect failed: %s\n", cp != NULLCHAR ? cp : "");
- #endif
- log (cb->s, "SMTP %s Connect failed: %s", psocket (&fsocket),
- cp != NULLCHAR ? cp : "");
- goto quit;
- }
- } else
- goto quit;
- }
- connected:
- kalarm (0L);
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf ("Connected\n");
- #endif
- kwait (NULL);
-
- #ifdef LZW
- rcode = getresp (cb, cb->buf, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
-
- #ifdef __GNUC__
- ampr = (((fsocket.sin_addr.s_addr & 0xff000000LU) >> 24) == 44);
- #else
- ampr = (((fsocket.sin_addr.s_addr & 0xff000000) >> 24) == 44);
- #endif
-
- /* even if LZW is enabled, don't use it for local connections
- or for hosts not on net 44 */
- if (Smtpclzw && ampr && strncmp (&cb->buf[4], Hostname, strlen (Hostname))) {
- char cpp[LINELEN];
-
- sendcmd (cb, "XLZW %d %d\n", Lzwbits, Lzwmode);
- usflush (cb->s);
- if (recvline (cb->s, (unsigned char *) cpp, sizeof (cpp)) == -1)
- goto quit;
- rip (cpp);
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf (smtp_recv, cpp); /* Display to user */
- #endif
- rcode = lzwmode = lzwbits = 0;
- sscanf (cpp, "%d %d %d", &rcode, &lzwbits, &lzwmode);
- if ((rcode >= 200) && (rcode < 300)) {
- smtpbatch = 1;
- if (lzwmode != Lzwmode || lzwbits != Lzwbits) {
- lzwmode = LZWCOMPACT;
- lzwbits = LZWBITS;
- }
- lzwinit (cb->s, lzwbits, lzwmode);
- } else
- smtpbatch = Smtpbatch;
- } else
- smtpbatch = Smtpbatch;
- #else
- smtpbatch = Smtpbatch;
- if (!smtpbatch) {
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
- }
- #endif
- /* Say HELO */
- sendcmd (cb, "HELO %s\n", Hostname);
- if (!smtpbatch) {
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
- }
- do { /* For each message... */
-
- kwait (NULL);
- /* if this file open fails, skip it */
- if ((cb->tfile = fopen (cb->tname, READ_TEXT)) == NULLFILE)
- continue;
- if (!filelength (fileno (cb->tfile))) {
- log (-1, "Skipping null length SMTP data file");
- (void) fclose (cb->tfile);
- cb->tfile = NULLFILE;
- (void) unlink (cb->tname);
- (void) unlink (cb->wname); /* unlink workfile */
- continue;
- }
- /* Send MAIL and RCPT commands */
- sendcmd (cb, "MAIL FROM:<%s>\n", cb->jobq->from);
- if (!smtpbatch) {
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
- }
- rcpts = 0;
- goodrcpt = 0;
- for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next) {
- sendcmd (cb, "RCPT TO:<%s>\n", tp->val);
- if (!smtpbatch) {
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1)
- goto quit;
- if (rcode < 400)
- goodrcpt = 1; /* At least one good */
- }
- rcpts++;
- }
- /* Send DATA command */
- sendcmd (cb, "DATA\n");
- kwait (NULL);
- if (!smtpbatch) {
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
- }
- if (smtpbatch) {
- /* Now wait for the responses to come back. The first time
- * we do this, we wait first for the start banner and
- * HELO response. In any case, we wait for the response to
- * the MAIL command here.
- */
- #ifdef LZW
- for (i = init ? 2 : 1; i > 0; i--) {
- #else
- for (i = init ? 3 : 1; i > 0; i--) {
- #endif
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
- }
- init = 0;
-
- /* Now process the responses to the RCPT commands */
- for (i = rcpts; i != 0; i--) {
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1)
- goto quit;
- if (rcode < 400)
- goodrcpt = 1; /* At least one good */
- }
- kwait (NULL);
- /* And finally get the response to the DATA command.
- * Some servers will return failure here if no recipients
- * are valid, some won't.
- */
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1 || rcode >= 400)
- goto quit;
-
- /* check for no good rcpt on the list */
- if (goodrcpt == 0) {
- sendcmd (cb, ".\n"); /* Get out of data mode */
- goto quit;
- }
- }
- /* Send the file. This also closes it */
- (void) smtpsendfile (cb);
-
- /* Wait for the OK response */
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1)
- goto quit;
- kwait (NULL);
- if ((rcode >= 400) && (rcode < 500)) /* temp failure? */
- check_qtime (cb);
- if ((rcode >= 200 && rcode < 300) || rcode >= 500) {
- /* if a good transfer or permanent failure remove job */
-
- if (cb->errlog != NULLLIST)
- retmail (cb);
- #ifdef STATS_MSG
- STATS_addmsg (1, 1);
- #endif
- /* Unlink the textfile */
- (void) unlink (cb->tname);
- (void) unlink (cb->wname); /* unlink workfile */
- log (cb->s, "SMTP sent job %s To: %s From: %s",
- cb->jobq->jobname, cb->jobq->to->val, cb->jobq->from);
- }
- if (cb->jobq->next != NULLJOB) {
- /*
- * Reset the remote sendmail's state.
- * This is just to deal with certain stupidities in early
- * versions of sendmail 8.6. We don't check the value of
- * rcode because we don't really care what it is and some
- * supposedly SMTP compliant mailers don't recognize RSET.
- */
- sendcmd (cb, "RSET\n");
- /* Wait for response */
- rcode = getresp (cb, NULLCHAR, 200);
- if (rcode == -1)
- goto quit;
- }
- } while (next_job (cb));
- quit:
- kwait (NULL);
- sendcmd (cb, "QUIT\n");
- check_qtime (cb);
- if (cb->errlog != NULLLIST) {
- retmail (cb);
- (void) unlink (cb->wname); /* unlink workfile */
- (void) unlink (cb->tname); /* unlink text */
- }
- close_s (cb->s);
- if (cb->tfile != NULLFILE)
- (void) fclose (cb->tfile);
- cb->lock = 0;
- del_session (cb);
- }
-
-
-
- /* check if msg stayed too long in the mqueue */
- static void
- check_qtime (register struct smtpcli *cb)
- {
- struct stat tstat;
- time_t now;
- char tmp[80];
-
- if (cb == NULLSMTPCLI || cb->jobq == NULLJOB)
- return;
-
- if (Sdtimer && cb->errlog == NULLLIST) {
- (void) time (&now);
- if (cb->tfile == NULLFILE)
- if ((cb->tfile = fopen (cb->tname, READ_TEXT)) == NULLFILE)
- return;
-
- (void) fstat (fileno (cb->tfile), &tstat);
- (void) fclose (cb->tfile);
- cb->tfile = NULLFILE;
- if ((now - tstat.st_ctime) > (time_t) (Sdtimer * 3600L)) {
- sprintf (tmp, " >>> Your message could not be delivered for %d hour(s); giving up!", Sdtimer);
- logerr (cb, tmp);
- }
- }
- return;
- }
-
-
-
- /* free the message struct and data */
- static void
- del_session (register struct smtpcli *cb)
- {
- register struct smtp_job *jp, *tp;
- register int i;
-
- if (cb == NULLSMTPCLI)
- return;
- for (i = 0; i < MAXSESSIONS; i++)
- if (cli_session[i] == cb) {
- cli_session[i] = NULLSMTPCLI;
- break;
- }
- free (cb->wname);
- free (cb->tname);
- free (cb->destname);
- for (jp = cb->jobq; jp != NULLJOB; jp = tp) {
- tp = jp->next;
- del_job (jp);
- }
- del_list (cb->errlog);
- free ((char *) cb);
- Smtpsessions--; /* number of connections active */
- }
-
-
-
- static void
- del_job (register struct smtp_job *jp)
- {
- if (*jp->jobname != '\0')
- rmlock (Mailqdir, jp->jobname);
- free (jp->from);
- del_list (jp->to);
- free ((char *) jp);
- }
-
-
-
- /* delete a list of list structs */
- void
- del_list (struct list *lp)
- {
- register struct list *tp, *tp1;
-
- for (tp = lp; tp != NULLLIST; tp = tp1) {
- tp1 = tp->next;
- free (tp->val);
- free (tp->orig);
- free ((char *) tp);
- }
- }
-
-
-
- /* stub for calling mdaemon to return message to sender */
- static void
- retmail (struct smtpcli *cb)
- {
- FILE *infile;
-
- #ifdef SMTPTRACE
- if (Smtptrace > 5)
- tcmdprintf ("smtp job %s returned to sender\n", cb->wname);
- #endif
- if ((infile = fopen (cb->tname, READ_TEXT)) == NULLFILE)
- return;
- /* mdaemon(infile,cb->jobq->from,cb->errlog,1); */
- (void) mdaemon (infile, "sysop", cb->errlog, 1);
- (void) fclose (infile);
- }
-
-
-
- /* look to see if a smtp control block exists for this ipdest */
- static struct smtpcli *
- lookup (uint32 destaddr)
- {
- register int i;
-
- for (i = 0; i < MAXSESSIONS; i++) {
- if (cli_session[i] == NULLSMTPCLI)
- continue;
- if (cli_session[i]->ipdest == destaddr)
- return cli_session[i];
- }
- return NULLSMTPCLI;
- }
-
-
-
- /* create a new smtp control block */
- static struct smtpcli *
- newcb (void)
- {
- register int i;
- register struct smtpcli *cb;
-
- for (i = 0; i < MAXSESSIONS; i++) {
- if (cli_session[i] == NULLSMTPCLI) {
- cb = (struct smtpcli *) callocw (1, sizeof (struct smtpcli));
-
- cb->wname = mallocw ((unsigned) strlen (Mailqdir) + JOBNAME);
- cb->tname = mallocw ((unsigned) strlen (Mailqdir) + JOBNAME);
- cli_session[i] = cb;
- Smtpsessions++; /* number of connections active */
- return (cb);
- }
- }
- return NULLSMTPCLI;
- }
-
-
-
- static void
- execjobs (void)
- {
- register struct smtpcli *cb;
- register int i, insave, outsave;
-
- for (i = 0; i < MAXSESSIONS; i++) {
- kwait (NULL);
- cb = cli_session[i];
- if (cb == NULLSMTPCLI)
- continue;
- if (cb->lock)
- continue;
-
- sprintf (cb->tname, "%s/%s.txt", Mailqdir, cb->jobq->jobname);
- sprintf (cb->wname, "%s/%s.wrk", Mailqdir, cb->jobq->jobname);
-
- /* This solves the nasty hack in mailbox.c, from Mark ve3dte */
- insave = Curproc->input;
- outsave = Curproc->output;
- Curproc->input = -1;
- Curproc->output = -1;
- /* Now we can call newproc with null parent sockets! */
- (void) newproc ("smtp_send", 1024, smtp_send, 0, cb, NULL, 0);
- /* now restore parent sockets so parent can continue */
- Curproc->input = insave;
- Curproc->output = outsave;
-
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf ("Trying Connection to %s\n", inet_ntoa (cb->ipdest));
- #endif
- }
- }
-
-
-
- /* add this job to control block queue */
- static struct smtp_job *
- setupjob (struct smtpcli *cb, char *id, char *from)
- {
- register struct smtp_job *p1, *p2;
-
- p1 = (struct smtp_job *) callocw (1, sizeof (struct smtp_job));
-
- p1->from = strdup (from);
- strncpy (p1->jobname, id, 9);
- /* now add to end of jobq */
- if ((p2 = cb->jobq) == NULLJOB)
- cb->jobq = p1;
- else {
- while (p2->next != NULLJOB)
- p2 = p2->next;
- p2->next = p1;
- }
- return p1;
- }
-
-
-
- /* called to advance to the next job */
- static int
- next_job (cb)
- register struct smtpcli *cb;
- {
- register struct smtp_job *jp;
-
- jp = cb->jobq->next;
- del_job (cb->jobq);
- /* remove the error log of previous message */
- del_list (cb->errlog);
- cb->errlog = NULLLIST;
- cb->jobq = jp;
- if (jp == NULLJOB)
- return 0;
- sprintf (cb->tname, "%s/%s.txt", Mailqdir, jp->jobname);
- sprintf (cb->wname, "%s/%s.wrk", Mailqdir, jp->jobname);
- #ifdef SMTPTRACE
- if (Smtptrace > 5)
- tcmdprintf ("sending job %s\n", jp->jobname);
- #endif
- return 1;
- }
-
-
-
- /* Mail routing function. For now just use the hosts file */
- uint32
- mailroute (char *dest)
- {
- uint32 destaddr = 0L;
- #ifdef HOPPER
- struct route *rp;
- #endif
-
- #ifdef SMTPTRACE
- if (Smtptrace > 6)
- tcmdprintf ("MX lookup for = %s\n", dest);
- #endif
-
- #ifdef HOPPER
- if (*dest == '\0') {
- #ifdef SMTPTRACE
- if (Smtptrace > 6)
- tcmdprintf ("Local mail\n");
- #endif
- return Ip_addr;
- }
- #endif
-
- /* look up address or use the gateway */
- if (UseMX) {
- destaddr = resolve_mx (dest);
- #ifdef SMTPTRACE
- if (Smtptrace > 6)
- tcmdprintf ("MX lookup returned = %s\n", inet_ntoa (destaddr));
- #endif
- }
- kwait (NULL);
- if (destaddr == 0L)
- if ((destaddr = resolve (dest)) == 0L)
- if (Gateway != 0)
- destaddr = Gateway; /* Use the gateway */
-
- #ifdef SMTPTRACE
- if (Smtptrace > 6)
- tcmdprintf ("Address resolver returned = %s\n", inet_ntoa (destaddr));
- #endif
-
- #ifdef HOPPER
- if (UseHopper && (destaddr != Ip_addr)) {
- if ((rp = rt_lookup (destaddr)) != NULLROUTE)
- if (rp->gateway != 0L)
- destaddr = rp->gateway;
- #ifdef SMTPTRACE
- if (Smtptrace > 6)
- tcmdprintf ("Hopper returned = %s\n", inet_ntoa (destaddr));
- #endif
- }
- #endif
-
- #ifdef SMTPTRACE
- if (Smtptrace > 6)
- tcmdprintf ("Mailroute returned = %s\n", inet_ntoa (destaddr));
- #endif
- return destaddr;
- }
-
-
-
- /* save line in error list */
- static void
- logerr (struct smtpcli *cb, char *line)
- {
- register struct list *lp, *tp;
-
- tp = (struct list *) callocw (1, sizeof (struct list));
-
- tp->val = strdup (line);
- /* find end of list */
- if ((lp = cb->errlog) == NULLLIST)
- cb->errlog = tp;
- else {
- while (lp->next != NULLLIST)
- lp = lp->next;
- lp->next = tp;
- }
- }
-
-
-
- static int
- smtpsendfile (register struct smtpcli *cb)
- {
- int error = 0;
- int lastlineCR = 1;
-
- strcpy (cb->buf, "\n");
- while (fgets (cb->buf, sizeof (cb->buf), cb->tfile) != NULLCHAR) {
- /* Escape a '.' character at the beginning of a line */
- if (*(cb->buf) == '.')
- usputc (cb->s, '.');
- usputs (cb->s, cb->buf);
- /* Did this line end in CR? */
- lastlineCR = (cb->buf[strlen (cb->buf) - 1] == '\n');
- kwait (NULL);
- }
- (void) fclose (cb->tfile);
- cb->tfile = NULLFILE;
- /* Send the end-of-message command */
- if (lastlineCR)
- sendcmd (cb, ".\n");
- else
- sendcmd (cb, "\n.\n");
- return error;
- }
-
-
-
- /* do a printf() on the socket with optional local tracing */
- static void
- sendcmd (struct smtpcli *cb, const char *fmt,...)
- {
- va_list args;
-
- va_start (args, fmt); /*lint !e718 !e746 */
- (void) vsprintf (cb->buf, fmt, args);
- #ifdef SMTPTRACE
- if (Smtptrace) {
- tcmdprintf ("smtp sent: ");
- #ifdef UNIX
- sm_status (0, cb->buf);
- #else
- (void) usvprintf (Curproc->output, fmt, args);
- #endif
- }
- #endif
- usputs (cb->s, cb->buf);
- va_end (args);
- }
-
-
-
- /* Wait for, read and display response from server. Return the result code. */
- static int
- getresp (
- struct smtpcli *cb,
- char *usebuf,
- int mincode /* Keep reading until at least this code comes back */
- ) {
- int rval;
- char buf[LINELEN], *line;
-
- line = (usebuf) ? usebuf : buf;
- usflush (cb->s);
- for (;;) {
- kwait (NULL);
- /* Get line */
- kalarm (Smtpt4 * 1000L); /* set a timeout */
- if (recvline (cb->s, (unsigned char *) line, LINELEN) == -1) {
- kalarm (0L); /* reset a timeout */
- log (cb->s, "SMTP Client timeout waiting for response");
- rval = -1;
- break;
- }
- kalarm (0L); /* reset a timeout */
- rip (line); /* Remove cr/lf */
- rval = atoi (line);
- #ifdef SMTPTRACE
- if (Smtptrace)
- tcmdprintf (smtp_recv, line); /* Display to user */
- #endif
- if (rval >= 500) { /* Save permanent error replies */
- char tmp[LINELEN];
-
- if (cb->errlog == NULLLIST) {
- sprintf (tmp, "While talking to %s:",
- cb->destname);
- logerr (cb, tmp);
- }
- if (cb->buf[0] != '\0') { /* Save offending command */
- rip (cb->buf);
- sprintf (tmp, ">>> %s", cb->buf);
- logerr (cb, tmp);
- cb->buf[0] = '\0';
- }
- sprintf (tmp, "<<< %s", line);
- logerr (cb, tmp); /* save the error reply */
- }
- /* Messages with dashes are continued */
- if (line[3] != '-' && rval >= mincode)
- break;
- }
- return rval;
- }
-